Branch data Line data Source code
1 : : /**@file 2 : : * This file is part of the event library; it contains the implementation of the 3 : : * thread-local event loop functions. 4 : : * 5 : : * @see lely/ev/thrd_loop.h 6 : : * 7 : : * @copyright 2018-2019 Lely Industries N.V. 8 : : * 9 : : * @author J. S. Seldenthuis <jseldenthuis@lely.com> 10 : : * 11 : : * Licensed under the Apache License, Version 2.0 (the "License"); 12 : : * you may not use this file except in compliance with the License. 13 : : * You may obtain a copy of the License at 14 : : * 15 : : * http://www.apache.org/licenses/LICENSE-2.0 16 : : * 17 : : * Unless required by applicable law or agreed to in writing, software 18 : : * distributed under the License is distributed on an "AS IS" BASIS, 19 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 : : * See the License for the specific language governing permissions and 21 : : * limitations under the License. 22 : : */ 23 : : 24 : : #include "ev.h" 25 : : #include <lely/ev/exec.h> 26 : : #include <lely/ev/task.h> 27 : : #include <lely/ev/thrd_loop.h> 28 : : 29 : : #include <assert.h> 30 : : #include <stdint.h> 31 : : 32 : : static void ev_thrd_loop_exec_on_task_init(ev_exec_t *exec); 33 : : static void ev_thrd_loop_exec_on_task_fini(ev_exec_t *exec); 34 : : static int ev_thrd_loop_exec_dispatch(ev_exec_t *exec, struct ev_task *task); 35 : : static void ev_thrd_loop_exec_defer(ev_exec_t *exec, struct ev_task *task); 36 : : static size_t ev_thrd_loop_exec_abort(ev_exec_t *exec, struct ev_task *task); 37 : : static void ev_thrd_loop_exec_run(ev_exec_t *exec, struct ev_task *task); 38 : : 39 : : // clang-format off 40 : : static const struct ev_exec_vtbl ev_thrd_loop_exec_vtbl = { 41 : : &ev_thrd_loop_exec_on_task_init, 42 : : &ev_thrd_loop_exec_on_task_fini, 43 : : &ev_thrd_loop_exec_dispatch, 44 : : &ev_thrd_loop_exec_defer, 45 : : &ev_thrd_loop_exec_defer, 46 : : &ev_thrd_loop_exec_abort, 47 : : &ev_thrd_loop_exec_run 48 : : }; 49 : : // clang-format on 50 : : 51 : : /// A thread-local event loop. 52 : : struct ev_thrd_loop { 53 : : /// The queue of pending tasks. 54 : : struct sllist queue; 55 : : /** 56 : : * The number of pending tasks. This equals the number tasks in #queue 57 : : * plus the number of calls to ev_exec_on_task_init() minus those to 58 : : * ev_exec_on_task_init(). ev_thrd_loop_stop() is called once this value 59 : : * reaches 0. 60 : : */ 61 : : size_t ntasks; 62 : : /// A flag specifying whether the event loop is stopped. 63 : : int stopped; 64 : : /// A flag specifying whether ev_exec_run() is running on this thread. 65 : : int running; 66 : : }; 67 : : 68 : : /// Returns a pointer to the thread-local event loop. 69 : : static struct ev_thrd_loop *ev_thrd_loop(void); 70 : : 71 : : static void ev_thrd_loop_on_task_init(struct ev_thrd_loop *loop); 72 : : static void ev_thrd_loop_on_task_fini(struct ev_thrd_loop *loop); 73 : : 74 : : ev_exec_t * 75 : 2 : ev_thrd_loop_get_exec(void) 76 : : { 77 : : static ev_exec_t ev_thrd_loop_exec = &ev_thrd_loop_exec_vtbl; 78 : : 79 : 2 : return &ev_thrd_loop_exec; 80 : : } 81 : : 82 : : void 83 : 0 : ev_thrd_loop_stop(void) 84 : : { 85 : 0 : ev_thrd_loop()->stopped = 1; 86 : 0 : } 87 : : 88 : : int 89 : 4 : ev_thrd_loop_stopped(void) 90 : : { 91 : 4 : return ev_thrd_loop()->stopped; 92 : : } 93 : : 94 : : void 95 : 1 : ev_thrd_loop_restart(void) 96 : : { 97 : 1 : ev_thrd_loop()->stopped = 0; 98 : 1 : } 99 : : 100 : : size_t 101 : 3 : ev_thrd_loop_run(void) 102 : : { 103 : 3 : size_t n = 0; 104 [ + + ]: 16777236 : while (ev_thrd_loop_run_one()) 105 : 16777233 : n += n < SIZE_MAX; 106 : 3 : return n; 107 : : } 108 : : 109 : : size_t 110 : 16777236 : ev_thrd_loop_run_one(void) 111 : : { 112 : 16777236 : struct ev_thrd_loop *loop = ev_thrd_loop(); 113 : : 114 [ + + ]: 16777236 : if (loop->stopped) 115 : 3 : return 0; 116 : : 117 : : struct ev_task *task = 118 : 16777233 : ev_task_from_node(sllist_pop_front(&loop->queue)); 119 [ - + ]: 16777233 : if (!task) { 120 [ # # ]: 0 : if (!loop->ntasks) 121 : 0 : loop->stopped = 1; 122 : 0 : return 0; 123 : : } 124 : : 125 : : assert(task->exec); 126 : 16777233 : ev_exec_run(task->exec, task); 127 : : 128 : 16777233 : ev_thrd_loop_on_task_fini(ev_thrd_loop()); 129 : : 130 : 16777233 : return 1; 131 : : } 132 : : 133 : : static void 134 : 6 : ev_thrd_loop_exec_on_task_init(ev_exec_t *exec) 135 : : { 136 : : assert(exec == ev_thrd_loop_get_exec()); 137 : : (void)exec; 138 : : 139 : 6 : ev_thrd_loop_on_task_init(ev_thrd_loop()); 140 : 6 : } 141 : : 142 : : static void 143 : 6 : ev_thrd_loop_exec_on_task_fini(ev_exec_t *exec) 144 : : { 145 : : assert(exec == ev_thrd_loop_get_exec()); 146 : : (void)exec; 147 : : 148 : 6 : ev_thrd_loop_on_task_fini(ev_thrd_loop()); 149 : 6 : } 150 : : 151 : : static int 152 : 0 : ev_thrd_loop_exec_dispatch(ev_exec_t *exec, struct ev_task *task) 153 : : { 154 : : assert(exec == ev_thrd_loop_get_exec()); 155 : : assert(task); 156 : : 157 [ # # ]: 0 : if (!task->exec) 158 : 0 : task->exec = exec; 159 : : assert(task->exec == exec); 160 : : 161 : 0 : struct ev_thrd_loop *loop = ev_thrd_loop(); 162 : 0 : ev_thrd_loop_on_task_init(loop); 163 [ # # ]: 0 : if (loop->running) { 164 : 0 : ev_thrd_loop_exec_run(exec, task); 165 : 0 : ev_thrd_loop_on_task_fini(loop); 166 : 0 : return 1; 167 : : } else { 168 : 0 : sllist_push_back(&loop->queue, &task->_node); 169 : 0 : return 0; 170 : : } 171 : : } 172 : : 173 : : static void 174 : 16777233 : ev_thrd_loop_exec_defer(ev_exec_t *exec, struct ev_task *task) 175 : : { 176 : : assert(exec == ev_thrd_loop_get_exec()); 177 : : assert(task); 178 : : 179 [ - + ]: 16777233 : if (!task->exec) 180 : 0 : task->exec = exec; 181 : : assert(task->exec == exec); 182 : : 183 : 16777233 : struct ev_thrd_loop *loop = ev_thrd_loop(); 184 : 16777233 : ev_thrd_loop_on_task_init(loop); 185 : 16777233 : sllist_push_back(&loop->queue, &task->_node); 186 : 16777233 : } 187 : : 188 : : static size_t 189 : 0 : ev_thrd_loop_exec_abort(ev_exec_t *exec, struct ev_task *task) 190 : : { 191 : : assert(exec == ev_thrd_loop_get_exec()); 192 : : (void)exec; 193 : : 194 : 0 : struct ev_thrd_loop *loop = ev_thrd_loop(); 195 : : 196 : 0 : size_t n = 0; 197 [ # # ]: 0 : if (!task) { 198 [ # # ]: 0 : while (sllist_pop_front(&loop->queue)) { 199 : 0 : ev_thrd_loop_on_task_fini(loop); 200 : 0 : n += n < SIZE_MAX; 201 : : } 202 [ # # ]: 0 : } else if (sllist_remove(&loop->queue, &task->_node)) { 203 : 0 : ev_thrd_loop_on_task_fini(loop); 204 : 0 : n++; 205 : : } 206 : 0 : return n; 207 : : } 208 : : 209 : : static void 210 : 16777233 : ev_thrd_loop_exec_run(ev_exec_t *exec, struct ev_task *task) 211 : : { 212 : : assert(exec == ev_thrd_loop_get_exec()); 213 : : assert(task); 214 : : 215 [ - + ]: 16777233 : if (!task->exec) 216 : 0 : task->exec = exec; 217 : : assert(task->exec == exec); 218 : : 219 : 16777233 : struct ev_thrd_loop *loop = ev_thrd_loop(); 220 : : 221 [ + - ]: 16777233 : if (task->func) { 222 : 16777233 : int running = loop->running; 223 : 16777233 : loop->running = 1; 224 : : 225 : 16777233 : task->func(task); 226 : : 227 : : // cppcheck-suppress redundantAssignment 228 : 16777233 : loop->running = running; 229 : : } 230 : 16777233 : } 231 : : 232 : : static struct ev_thrd_loop * 233 : 67108952 : ev_thrd_loop(void) 234 : : { 235 : : #if LELY_NO_THREADS 236 : : static struct ev_thrd_loop *loop; 237 : : #else 238 : : static _Thread_local struct ev_thrd_loop *loop; 239 : : #endif 240 [ + + ]: 67108952 : if (!loop) { 241 : : #if LELY_NO_THREADS 242 : : static struct ev_thrd_loop loop_; 243 : : #else 244 : : static _Thread_local struct ev_thrd_loop loop_; 245 : : #endif 246 : 2 : sllist_init(&loop_.queue); 247 : 2 : loop_.ntasks = 0; 248 : 2 : loop_.stopped = 0; 249 : 2 : loop_.running = 0; 250 : 2 : loop = &loop_; 251 : : } 252 : 67108952 : return loop; 253 : : } 254 : : 255 : : static void 256 : 16777239 : ev_thrd_loop_on_task_init(struct ev_thrd_loop *loop) 257 : : { 258 : : assert(loop); 259 : : 260 : 16777239 : loop->ntasks++; 261 : 16777239 : } 262 : : 263 : : static void 264 : 16777239 : ev_thrd_loop_on_task_fini(struct ev_thrd_loop *loop) 265 : : { 266 : : assert(loop); 267 : : assert(loop->ntasks); 268 : : 269 [ + + ]: 16777239 : if (!--loop->ntasks) 270 : 3 : loop->stopped = 1; 271 : 16777239 : }